package forker;

our $sleep_before_next_loop=1;
our %teams_pid;
our %teams_obj;

use POSIX ":sys_wait_h";
use Util::Logger;
use db_config;
use ExploitFarm_SQL;

sub new
{
    my $class=shift;
    my $exploit;
    my $db;
    my $obj=
	{
	    @_
	};
    $$obj{'child'}=0;
    $exploit=$$obj{exploit};
    $$obj{'custom_log'}=new Util::Logger("getters/$exploit/custom_log.txt");
    $$obj{'service_log'}=new Util::Logger("getters/$exploit/service_log.txt");
    $$obj{'global_log'}=new Util::Logger("getters/global_log.txt");
    print "DB: $db_host,$db_port,$db_name,$db_login,$db_pass\n";
    my $db=new ExploitFarm_SQL($db_host,$db_port,$db_name,$db_login,$db_pass);
    $db->connect();
    $$obj{'db'}=$db;
    require "getters/$exploit/exploit.pm";
    import exploit qw(get_flags);

    bless $obj;
}

sub run
{
    my ($self)=@_;
    my $pid=fork();
    my $dir=$self->{exploit_dir};
    return undef if (!defined($pid));
    $self->{run}=1;
    if($pid)
    {
	$self->{pid}=$pid;
    }else
    {
	chdir $dir if (defined $dir);
	#$self->{started}=1;
	$self->_start();
	$SIG{CHLD} = \&_REAPER;
	$SIG{TERM} = \&_TERM;
	$self->_serve();
	$self->{'child'}=1;
    }
    return $pid;
}

sub stop
{
    my ($self)=@_;
    my $gl=$self->{global_log};
    my $pid=$self->{pid};

    if ($self->{run}!=1)
    {
	return 0;
    }
    $SIG{CHLD}=DEFAULT;
    if (defined $pid)
    {
	$gl->debug("Sending TERM signal: $pid");
	kill TERM,$pid;
	sleep 2;
	$gl->debug("Sending KILL signal: $pid");
	kill 9,$pid;
    }
}

#start();
#$SIG{CHLD} = \&REAPER;
#serve();

sub _TERM
{
    $SIG{CHLD}=DEFAULT;
    my @pids=keys %teams_pid;
    kill TERM, $_ for (@pids);
#    kill 9, $_ for (@pids);
    exit(0);
}

sub _TERM_CHILD
{
#    $me->{global_log}->("Softly stopping");
    $me->{run}=0;
}

sub _REAPER
{
    my $child;
    my $team;
    my $self;
    my $sl;
    # If a second child dies while in the signal handler caused by the
    # first death, we won't get another signal. So must loop here else
    # we will leave the unreaped child as a zombie. And the next time
    # two children die we get another zombie. And so on.
    while (($child = waitpid(-1,WNOHANG)) > 0)
    {
	#$Kid_Status{$child} = $?;
	my $status=$?;
	$team=$teams_pid{$child};
	$self=$teams_obj{$child};
	$sl=$self{'service_log'};
	$sl->debug("-- Team $team loop end. Status: $status");
	$sl->debug("Somthing wrong in script with team $team") if ($status != 0);
	delete $teams_pid{$child};
	delete $teams_obj{$child};

	$self->_doit_start($team);
    } 
    $SIG{CHLD} = \&_REAPER;  # still loathe sysV
}
#$SIG{CHLD} = \&_REAPER;
#

sub _doit_one
{
    my ($self,$team)=@_;
    my $exploit=$self->{'exploit'};
    my $gl=$self->{'global_log'};
    my $sl=$self->{'service_log'};
    my $flags;
    while($self->{run})
    {
        eval
	{
		$flags=get_flags($team,$self);
	};
	if($@ =~ /Undefined subroutine/)
	{
	    eval("\$flags=exploit::get_flags(\$team,\$self);");
	    if ($@)
	    {
		$gl->alert($@);
		$sl->error($@);
	    }
	}elsif($@)
	{
	    $gl->alert($@);
	    $sl->error($@);
	    return $@;
	}
	$db->put_flags($self->{exploit},$my_team,$flags) if (defined $flags);

	$sl->debug(">>>Team $team next loop!");
        $sl->debug("\t\tTeam $team ends loop and relaxing a bit before next");
	sleep($sleep_before_next_loop);
	$sl->debug("\t\tTeam $team > next loop!");
    }
    exit($status);
}

sub _doit_start
{
    my ($self,$team)=@_;
    my $exploit=$self->{exploit};
    my $gl=$self->{global_log};
    my $sl=$self->{service_log};
    my $pid=fork();
    if (!defined $pid)
    {
	$gl->alert("'$exploit': Internal Error: Can`t fork!");
	$sl->error("'$exploit': Internal Error: Can`t fork!");
	return 1;
    }
    
    if ($pid > 0) #parent
    {
	$teams_pid{$pid}=$team;
	$teams_obj{$pid}=$self;
    }else #child
    {
	our $me=$self;
	our $my_team=$team;
	$SIG{CHLD} = DEFAULT;
	$SIG{TERM} = \&_TERM_CHILD;
	$self->{child}=1;
	$self->_doit_one($team);
    }
    return 0;
}

sub _start
{
    my ($self)=@_;
    my $teams=$self->{teams};
    my $sl=$self->{service_log};
    $sl->debug("Starting...");
    for (@$teams)
    {
	$sl->debug("\tStarting team '$_'");
	$self->_doit_start($_);
    }
}

sub _serve
{
    while (1)
    {
	sleep 12000; #do nothing :) Just waiting for CHLD signal
    }
}

sub put_flag
{
    my ($self,$flag)=@_;
    my $db=$self->{db};
    my $exploit=$self->{exploit};
    my $sl=$self->{service_log};

    $sl->info("Put flag: '$flag'");
    $db->put_flags($exploit,$my_team,[$flag]);
}

sub DESTROY
{
    my $self=shift;
    $self->stop() if(!$self->{child});
}

1;
